#!/usr/bin/env node

/**
 * 解决本地依赖下无法找到间接依赖问题
 */
const fs = require('fs'),
  path = require('path')
// pkg = require(`${process.cwd()}/package.json`)

if (!fs.existsSync(`${process.cwd()}/.npmrc`)) {
  console.log('缺少 ".npmrc" 配置文件')
  return
}

// 递归创建目录 同步方法
function mkdirsSync (dirname) {
  if (fs.existsSync(dirname)) {
    return true
  } else {
    if (mkdirsSync(path.dirname(dirname))) {
      fs.mkdirSync(dirname)
      return true
    }
  }
}

const localDependencies = [],
  dependenciePackages = {}

function getLocalDependencies (dir) {
  const pkg = require(`${dir}/package.json`)
  for (let k in pkg.dependencies) {
    if (pkg.dependencies[k].startsWith('file:') || pkg.dependencies[k].startsWith('link:')) {
      const localDependencyProjectPath = path.resolve(process.cwd(), pkg.dependencies[k].split(':')[1])
      localDependencies.push(localDependencyProjectPath)
      getLocalDependencies(localDependencyProjectPath)
    }
  }
  for (let k in pkg.devDependencies) {
    if (pkg.devDependencies[k].startsWith('file:') || pkg.devDependencies[k].startsWith('link:')) {
      const localDependencyProjectPath = path.resolve(process.cwd(), pkg.devDependencies[k].split(':')[1])
      localDependencies.push(localDependencyProjectPath)
      getLocalDependencies(localDependencyProjectPath)
    }
  }
}

getLocalDependencies(process.cwd())

if (!Object.keys(localDependencies).length) {
  console.log('未找到本地依赖，停止构建')
  return
}

console.log('构建本地依赖映射关系...')

let npmrc = fs.readFileSync(`${process.cwd()}/.npmrc`, 'utf8')
  .replace(/\r/g, '')
  .split('\n')
  .filter(line => line.trim().startsWith('public-hoist-pattern'))
  .map(line => line.split('=')[1].trim())


function listDependencieDir (absoluteDir) {
  fs.readdirSync(absoluteDir).forEach(file => {
    let filepath = path.join(absoluteDir, file)
    if (file == '.pnpm') {
      fs.readdirSync(filepath).forEach(pfile => {
        let _pfile = pfile
        if (fs.statSync(`${filepath}/${pfile}`).isFile() || pfile == 'node_modules') {
          return
        }
        if (new RegExp(/@\d+.?\d+.?\d+.*$/g).test(pfile.split('_')[0])) {
          _pfile = pfile.split('_')[0].replace('+', '/')
        }
        _pfile = _pfile.substring(0, _pfile.lastIndexOf('@'))
        getPackageJsonFiles(`${filepath}/${pfile}/node_modules/${_pfile}`)
      })
    } else if (!file.startsWith('.')) {
      if (file.startsWith('@')) {
        fs.readdirSync(filepath).forEach(pfile => {
          getPackageJsonFiles(`${filepath}/${pfile}`)
        })
      } else {
        getPackageJsonFiles(filepath)
      }
    }
  })
}

function getPackageJsonFiles (filepath) {
  if (!fs.existsSync(filepath)) {
    return
  }
  let pkg = require(filepath + '/package.json')
  if (fs.lstatSync(filepath).isSymbolicLink()) {
    dependenciePackages[pkg.name] = `#LINK#${fs.readlinkSync(filepath)}`
  } else {
    dependenciePackages[pkg.name] = filepath
  }
}

let localDependencieFiles = {}
localDependencies.forEach(localDependency => {
  listDependencieDir(`${localDependency}/node_modules`)
})

const hoists = {}

function findHoistPackage (keyword) {
  dependenciePackages[key] && (hoists[key] = dependenciePackages[key])
}

[...npmrc].forEach(key => {
  if (!key.includes('*')) {
    dependenciePackages[key] && (hoists[key] = dependenciePackages[key])
    return
  }

  if (key.endsWith('*')) {
    key = `${key.replace(/\*+/g, '(.+)?')}`
  } else {
    key = `${key.replace('*', '(.+)?')}$`
  }

  const reg = new RegExp(key)
  let keys = Object.keys(dependenciePackages).filter(k => reg.test(k))
  if (keys.length > 0) {
    keys.forEach(k => {
      hoists[k] = dependenciePackages[k]
    })
    // npmrc.splice(npmrc.indexOf(nkey), 1)
  }
})
Object.keys(hoists).forEach(key => {
  const dir = `${process.cwd()}/node_modules`
  if (fs.existsSync(`${dir}/${key}`)) {
    return
  }
  if (key.startsWith('@')) {
    let split = key.split('/')
    if (!fs.existsSync(`${dir}/${split[0]}`)) {
      mkdirsSync(`${dir}/${split[0]}`)
    }
  }
  fs.symlinkSync(hoists[key].replace('#LINK#', ''), `${dir}/${key}`, 'junction')
})